package com.wh.wisdomsite.xbox.server.interceptor;

import com.alibaba.fastjson.JSON;
import com.wh.wisdomsite.xbox.common.XboxRequestContext;
import com.wh.wisdomsite.xbox.core.interceptor.AbstractInterceptor;
import com.wh.wisdomsite.xbox.core.ServiceDispatcher;
import com.wh.wisdomsite.xbox.core.session.impl.SimpleSession;
import com.wh.wisdomsite.xbox.server.manager.SampleInvokeTimesController;
import com.wh.wisdomsite.xbox.server.model.LimitMethod;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Resource;


/***
 * 方法调用限制次数（调用频率）拦截器
 * @Package com.wh.wisdomsite.xbox.server.interceptor
 * @author 谢泽鹏
 * @date 2016年11月07日 下午15:10
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2016
 * @version V1.0
 */
public class MethodLimitInteceptor extends AbstractInterceptor {

    @Resource(name = "serviceDispatcher")
    private ServiceDispatcher serviceDispatcher;

    //限制访问KEY
    public static String LIMIT_KEY = "limit_key";
    //频率次数限制
    public static int LIMIT_TIMES = 20;
    //频率
    public static int LIMIT_RATE = 60 * 1000;
    //频率过大 限制时间
    public static long LIMIT_TIME = 60 * 1000;


    /**
     * 方法前拦截(调用服务之前)
     * @param xboxRequestContext
     */
    @Override
    public void beforeService(XboxRequestContext xboxRequestContext) {
        Map response = new HashMap();
        response.put("code","103");
        response.put("message","操作频繁，请稍后再试！");
        //限制bean
        LimitMethod limitMethod = getLimit(xboxRequestContext);
        String key = limitMethod.getMd5();
        SimpleSession session = (SimpleSession) xboxRequestContext.getXboxContext().getSessionManager().getSession(key);
        if(session==null){
            addLimit(xboxRequestContext);
        }else{
            Object limitKey = session.getAttribute(LIMIT_KEY);
            if(limitKey==null || limitKey.toString().equals("")){
                addLimit(xboxRequestContext);
            } else{
                LimitMethod _limitMethod = JSON.parseObject(limitKey.toString(), LimitMethod.class);
                if(_limitMethod.getMd5().equals(key)){
                    long currentTime = new Date().getTime();
                    //判断时间是否超过限制时间
                    if(currentTime-_limitMethod.getTime()>LIMIT_RATE){
                        //重新计算频率
                        addLimit(xboxRequestContext);
                    } else{
                        //判断次数是否超过限制
                        if(_limitMethod.getTimes()>=LIMIT_TIMES){
                            //是否正在罚时期间
                            if(_limitMethod.getLimitTime()<=0){
                                //添加罚时时间
                                _limitMethod.setLimitTime(currentTime);
                                addLimit(_limitMethod, xboxRequestContext);
                                //返回结果
                                xboxRequestContext.setXboxResponse(response);
                            }else{
                                //判断罚是时间是否过期
                                if(currentTime-_limitMethod.getLimitTime()>=LIMIT_TIME){
                                    removeLimit(xboxRequestContext);
                                } else{
                                    //返回结果
                                    xboxRequestContext.setXboxResponse(response);
                                }
                            }
                        }else{
                            //添加次数
                            _limitMethod.setTimes(_limitMethod.getTimes()+1);
                            addLimit(_limitMethod, xboxRequestContext);
                        }
                    }
                }else{
                    removeLimit(xboxRequestContext);
                }
            }
        }

    }


    /**
     * 方法后拦截(调用服务之后)
     * @param xboxRequestContext
     */
    @Override
    public void beforeResponse(XboxRequestContext xboxRequestContext) {

    }


    /**
     * 该方法返回true时才实施拦截，否则不拦截
     * @param xboxRequestContext
     * @return
     */
    @Override
    public boolean isMatch(XboxRequestContext xboxRequestContext) {
        String method = xboxRequestContext.getMethod();
        if(method.matches("[\\w.]*((uh.user.sendVerificationCode))")){
            return true;
        }
        return false;
    }

    /**
     * 获取方法限制bean
     * @param xboxRequestContext
     * @return
     */
    public LimitMethod getLimit(XboxRequestContext xboxRequestContext){
        //获取IP地址
        String ip = xboxRequestContext.getIp();
        //获取方法
        String method = xboxRequestContext.getMethod();
        //获取版本
        String version = xboxRequestContext.getVersion();
        //获取appKey
        String appKey = xboxRequestContext.getAppKey();
        //构建LimitMethod对象
        LimitMethod limitMethod = new LimitMethod();
        limitMethod.setIp(ip);
        limitMethod.setMethod(method);
        limitMethod.setVersion(version);
        //从serviceDispatcher中获取调用InvokeTimesController控制器类
        //得到服务方法调用频率
        Map<String, AtomicInteger> callRate = ((SampleInvokeTimesController) serviceDispatcher.getSecurityManager().getInvokeTimesController()).getAppMethodCallRate();
        //从容器获取频率
        AtomicInteger atomic = callRate.get(appKey + "$" + method + "$" + version);
        if(null != atomic)
            LIMIT_TIMES = atomic.intValue();
        return limitMethod;
    }

    /**
     * 添加方法限制bean
     * @param xboxRequestContext
     */
    public void addLimit(LimitMethod limitMethod, XboxRequestContext xboxRequestContext){
        //获取限制
        String key = limitMethod.getMd5();
        //放入session
        SimpleSession _session = new SimpleSession();
        _session.setAttribute(LIMIT_KEY,limitMethod);
        xboxRequestContext.addSession(key, _session);
    }

    public void addLimit(XboxRequestContext xboxRequestContext){
        LimitMethod limitMethod = getLimit(xboxRequestContext);
        addLimit(limitMethod, xboxRequestContext);
    }

    /**
     * 删除方法限制bean
     * @param xboxRequestContext
     */
    public void removeLimit(XboxRequestContext xboxRequestContext){
        //获取限制
        LimitMethod limitMethod = getLimit(xboxRequestContext);
        String key = limitMethod.getMd5();
        //删除
        xboxRequestContext.getXboxContext().getSessionManager().removeSession(key);
    }


}
